package com.zjs.express.mvc.servlet;

import com.zjs.express.common.ResultModel;
import com.zjs.express.mvc.common.Handler;
import com.zjs.express.mvc.common.HandlerMapping;
import com.zjs.express.mvc.enumType.ResponseType;
import com.zjs.express.utils.JsonUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 表示定义一个前端控制器，处理请求和加载配置映射
 */
public class DispatchServlet extends HttpServlet {
    private HandlerMapping handlerMapping = new HandlerMapping();

    /**
     * 调用时机在处理请求之前被调用，可以在这个方法中编写加载handle_mvc.xml配置文件，dom4j包解析xml
     *
     * @throws ServletException
     */
    @Override
    public void init() throws ServletException {
        //bean标签中class属性所指的类加载出来，并创建对象
        //1.加载读取handle_mvc.xml配置文件，dom4j包解析xml
        //InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("spring_mvc.xml");
        //通过全局参数进行读取文件（web.xml中配置）
        String xml = this.getServletConfig().getInitParameter("configLocation");
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(xml);
        //2、加载其中的数据（树状结构的数据）
        SAXReader saxReader = new SAXReader();
        try {
            //返回的是一个Document对象，是树状结构
            Document document = saxReader.read(inputStream);
            //获取xml文件的根
            Element rootElement = document.getRootElement();
            //获取根节点下面的所有子节点
            List<Element> elements = rootElement.elements();
            //保存创建出来对象的List
            List<Object> beans = new ArrayList<>();
            //获取元素下面的class属性
            for (Element element : elements) {
                String className = element.attributeValue("class");
                //根据类的路径创建类的对象
                Object bean = Class.forName(className).newInstance();
                //将创建出来的对象进行保存
                beans.add(bean);
            }
            System.out.println("配置文件读取的beans-->" + beans);
            //处理映射关系(将url和Handler进行绑定映射)
            handlerMapping.process(beans);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置编码
        req.setCharacterEncoding("utf-8");
        //获取请求的uri(包含contentPath--》/handle_mvc/hello.do)
        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        String url = uri.substring(contextPath.length());
        //通过url获取映射关系（Handler）
        Handler handler = handlerMapping.getHandler(url);
        if (handler != null) {
            //获取Handler中的方法
            Method method = handler.getMethod();
            //获取Handler中的对象
            Object bean = handler.getObject();
            //返回类型：文本、视图
            ResponseType responseType = handler.getType();
            //通过反射调用方法
            Object resultValue = null;
            try {
                //resultValue = method.invoke(bean);//无参
                //根据参数个数调取方法
                //获取参数列表
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length > 0) {
                    //有参数
                    Object[] params = new Object[2];
                    //只考虑两个参数request、response
                    for (Class<?> parameterType : parameterTypes) {
                        if (parameterType == HttpServletRequest.class) {
                            params[0] = req;
                        }
                        if (parameterType == HttpServletResponse.class) {
                            params[1] = resp;
                        }
                    }
                    resultValue = method.invoke(bean, params);
                } else {
                    //无参数
                    resultValue = method.invoke(bean);
                }
                System.out.println("访问路径(" + url + ")-->调取-->" + bean + "下面的-->" + method.getName());
                //返回的是对象
                if (resultValue instanceof ResultModel) {
                    ResultModel resultModel = (ResultModel) resultValue;
                    resp.setContentType("application/json;charset=utf-8");
                    resp.getWriter().write(JsonUtils.toJson(resultModel));
                } else { //返回的是字符串
                    String viewName = resultValue.toString();
                    //返回文本
                    if (responseType == ResponseType.TEXT) {
                        resp.setContentType("text/html;charset=utf-8");
                        resp.getWriter().write(viewName);
                    } else {
                        //返回视图
                        if (resultValue != null) {
                            //TODO 创建视图类，判断返回是视图还是字符串（ResponseBody注解）
                            if (viewName.startsWith("redirect:")) {
                                //重定向
                                //将视图名称完整的路径进行重定向：contextPath/url--》/handle_mvc/hello.do
                                if (viewName.indexOf("html") != -1) {
                                    resp.sendRedirect(req.getContextPath() + "/" + viewName.replace("redirect:", ""));
                                } else {
                                    resp.sendRedirect(req.getContextPath() + "/" + viewName.replace("redirect:", "") + ".html");
                                }
                            } else {
                                //转发
                                if (viewName.indexOf(".do") != -1) {
                                    req.getRequestDispatcher(viewName).forward(req, resp);
                                } else {
                                    req.getRequestDispatcher(viewName + ".html").forward(req, resp);
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
